package top.wjgyy.tools.ddns.controller;

import cn.hutool.core.lang.func.VoidFunc1;
import com.aliyuncs.alidns.model.v20150109.DescribeDomainRecordsResponse;
import com.aliyuncs.domain.model.v20180129.QueryDomainListResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.utils.StringUtils;
import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import top.wjgyy.tools.ddns.DDNSApplication;
import top.wjgyy.tools.ddns.config.ConfigReder;
import top.wjgyy.tools.ddns.enums.AliConstants;
import top.wjgyy.tools.ddns.job.DDNSJob;
import top.wjgyy.tools.ddns.model.IndexModel;
import top.wjgyy.tools.ddns.node.SystemTrayNode;
import top.wjgyy.tools.ddns.service.DDNSService;
import top.wjgyy.tools.ddns.view.IndexView;

import java.awt.*;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.stream.Collectors;

public class IndexController extends IndexView {

    //配置弹出框
    private JFXDialog configDialog;
    //系统托盘
    private SystemTrayNode tray;
    //DDNS主服务
    private DDNSService ddnsService;
    //加载动画
    private VBox loading;

    private IndexModel model;

    private Integer timeInterval = 1;

    private Scheduler scheduler;

    private JobDetail job;


    //域名列表
    private ObservableList<QueryDomainListResponse.Domain> domainComboItems = FXCollections.observableArrayList();
    //域名解析列表
    private ObservableList<DescribeDomainRecordsResponse.Record> domainRecordsComboItems = FXCollections.observableArrayList();

    private ObservableList<AliConstants> domainTypeComboItems = FXCollections.observableArrayList(AliConstants.getAllType());

    private ObservableList<String> cycleComboItems = FXCollections.observableArrayList("秒", "分", "时");

    private StringProperty cycleValue = new SimpleStringProperty("分");


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        //初始化系统托盘
        tray = SystemTrayNode.getInstance();
        setButtonType(true);
        runButton.setDisable(true);
        model = ConfigReder.getConfig("index.json", IndexModel.class);
        DDNSApplication.addCloseActions(a -> {
            ConfigReder.saveConfig("index.json", model);
        });
        if (!StringUtils.isEmpty(model.getAccessKeyId()) && ! StringUtils.isEmpty(model.getSecret())){
            ddnsService = DDNSService.getInstance(model.getAccessKeyId(), model.getSecret());
            if (model.getHistoryValue().size() > 0) {
                model.getHistoryValue().remove(0, model.getHistoryValue().size());
            }
            initData();
        }

        //数据绑定
        domainCombo.setItems(domainComboItems);
        domainCombo.valueProperty().bindBidirectional(model.domainComboValueProperty());
        domainRecordsCombo.setItems(domainRecordsComboItems);
        domainRecordsCombo.valueProperty().bindBidirectional(model.domainRecordsComboValueProperty());
        ipv4Field.textProperty().bindBidirectional(model.ipv4Property());
        ipv6Field.textProperty().bindBidirectional(model.ipv6Property());
        domainTypeCombo.setItems(domainTypeComboItems);
        domainTypeCombo.valueProperty().bindBidirectional(model.domainTypeComboValueProperty());
        recordsAdrressField.textProperty().bindBidirectional(model.recordsAdrressValueProperty());
        timeSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 100));
        historyArea.setItems(model.getHistoryValue());
        cycleCombo.setItems(cycleComboItems);
        cycleCombo.valueProperty().bindBidirectional(cycleValue);

        timeSpinner.valueProperty().addListener((observable, oldValue, newValue) -> {
            timeInterval = newValue;
        });
        //域名选项卡展示定义
        domainCombo.setButtonCell(domainListCell());
        domainCombo.setCellFactory(param -> domainListCell());
        //域名选项卡显示
        domainCombo.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) return;
            domainRecordsCombo.setDisable(true);
            new Thread(() -> {
                try {
                    List<DescribeDomainRecordsResponse.Record> records = ddnsService.getDescribeDomainRecordsList(newValue.getDomainName())
                            .stream().filter(i -> AliConstants.getByType(i.getType()) != null).collect(Collectors.toList());
                    Platform.runLater(() -> {
                        domainRecordsComboItems.removeAll(domainRecordsComboItems);
                        domainRecordsComboItems.addAll(records);
                        if (domainRecordsComboItems.size() > 0) domainRecordsCombo.setDisable(false);
                    });
                } catch (ClientException e) {
                    e.printStackTrace();
                }
            }).start();
        });
        //域名选项列表数据变更操作
        domainComboItems.addListener((ListChangeListener<? super QueryDomainListResponse.Domain>) c -> {
            while (c.next()) {
                model.setDomainComboValue(null);;
                if (!c.wasAdded()) return;
                Platform.runLater(() -> {
                    // domainComboValue.set(null);
                    model.setDomainComboValue(c.getAddedSubList().get(0));
                });
            }
        });
        //域名解析列表数据变更操作
        domainRecordsCombo.setCellFactory(param ->domainRecordsListCell());
        domainRecordsCombo.setButtonCell(domainRecordsListCell());
        domainRecordsComboItems.addListener((ListChangeListener<? super DescribeDomainRecordsResponse.Record>) c -> {
            while (c.next()) {
                model.setDomainRecordsComboValue(null);
                if (!c.wasAdded()) return;
                Platform.runLater(() -> {
                    model.setDomainRecordsComboValue(c.getAddedSubList().get(0));
                });
            }
        });
        domainRecordsCombo.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) return;
            AliConstants constants = AliConstants.getByType(newValue.getType());
            model.setDomainTypeComboValue(constants);
            model.setRecordsAdrressValue(newValue.getValue());
        });
        //解析类型
        domainTypeCombo.setButtonCell(domainTypeListCell(false));
        domainTypeCombo.setCellFactory(param -> domainTypeListCell(true));
    }

    /**
     * 按钮显示
     * @param type true：显示执行按钮，隐藏取消执行按钮  false：隐藏执行按钮，显示取消执行按钮
     */
    private void setButtonType(boolean type){
        runButton.setVisible(type);
        runButton.setManaged(type);
        cancelButton.setVisible(!type);
        cancelButton.setManaged(!type);
    }

    /**
     * 域名自定义加载
     * 由于Combobox特性会调用实体类的toString后展示，我们这里只需要展示域名信息，因此做此改变以获得更好展示效果
     *
     * @return
     */
    public ListCell<QueryDomainListResponse.Domain> domainListCell() {
        return new ListCell<QueryDomainListResponse.Domain>() {
            @Override
            protected void updateItem(QueryDomainListResponse.Domain item, boolean empty) {
                super.updateItem(item, empty);
                if (empty == false) {
                    this.setText(item.getDomainName());
                }
            }
        };
    }

    /**
     * 解析自定义展示
     *
     * @return
     */
    public ListCell<DescribeDomainRecordsResponse.Record> domainRecordsListCell() {
        return new ListCell<DescribeDomainRecordsResponse.Record>() {
            @Override
            protected void updateItem(DescribeDomainRecordsResponse.Record item, boolean empty) {
                super.updateItem(item, empty);
                if (empty == false) {
                    this.setText(item.getRR());
                }
            }
        };
    }
    /**
     * 解析自定义展示
     *
     * @return
     */
    public ListCell<AliConstants> domainTypeListCell(boolean showNotes) {
        return new ListCell<AliConstants>() {
            @Override
            protected void updateItem(AliConstants item, boolean empty) {
                super.updateItem(item, empty);
                if (empty == false) {
                    this.setText(showNotes ? item.getType() + "-" + item.getNotes() : item.getType());
                }
            }
        };
    }

    /**
     * 阿里SDK配置页面
     * @param event
     */
    public void showConfig(MouseEvent event) {
        if (!MouseButton.PRIMARY.equals(event.getButton()) || event.getClickCount() != 1) return;
        if (configDialog != null) {
            if (!stack.getChildren().contains(configDialog)) configDialog.show();
            return;
        }
        JFXDialogLayout layout = new JFXDialogLayout();
        AnchorPane titleBox = new AnchorPane();
        HBox title = new HBox();
        title.setAlignment(Pos.CENTER);
        Label ddns = new Label("设置");
        ddns.setFont(new Font(16));
        title.getChildren().add(ddns);
        JFXButton closeButton = new JFXButton();
        closeButton.getStyleClass().add("close_button");
        titleBox.getChildren().addAll(title, closeButton);
        AnchorPane.setTopAnchor(title, 0D);
        AnchorPane.setBottomAnchor(title, 0D);
        AnchorPane.setLeftAnchor(title, 0D);
        AnchorPane.setRightAnchor(title, 0D);

        AnchorPane.setTopAnchor(closeButton, 0D);
        AnchorPane.setBottomAnchor(closeButton, 0D);
        AnchorPane.setRightAnchor(closeButton, 0D);
        layout.setHeading(titleBox);

        VBox bodyBox = new VBox();
        bodyBox.setAlignment(Pos.CENTER_RIGHT);
        bodyBox.setPadding(new Insets(0, 10, 0, 0));
        bodyBox.setSpacing(20);
        Label accessKeyIdLable = new Label("accessKeyId:");
        TextField accessKeyIdField = new TextField();
        accessKeyIdField.textProperty().bindBidirectional(model.accessKeyIdProperty());
        accessKeyIdField.prefWidthProperty().bind(bodyBox.widthProperty().subtract(120));
        accessKeyIdLable.setGraphic(accessKeyIdField);
        accessKeyIdLable.setContentDisplay(ContentDisplay.RIGHT);
        Label secretLable = new Label("secret:");
        TextField secretField = new TextField();
        secretField.textProperty().bindBidirectional(model.secretProperty());
        secretField.prefWidthProperty().bind(bodyBox.widthProperty().subtract(120));
        secretLable.setGraphic(secretField);
        secretLable.setContentDisplay(ContentDisplay.RIGHT);
        JFXButton saveButton = new JFXButton("应用");
        saveButton.setButtonType(JFXButton.ButtonType.RAISED);
        // saveButton.setStyle("-fx-background-color: #e7e8e8");
        saveButton.setOnAction(e -> {
            ddnsService = DDNSService.getInstance(model.getAccessKeyId(), model.getSecret());
            if (loading == null) {
                loading = new VBox();
                loading.setStyle("-fx-background-color: rgba(0,0,0,0.1)");
                loading.setAlignment(Pos.CENTER);
                ProgressIndicator indicator = new ProgressIndicator();
                loading.getChildren().add(indicator);
            }
            stack.getChildren().add(loading);
            initData();
        });
        bodyBox.getChildren().addAll(accessKeyIdLable, secretLable, saveButton);
        layout.setBody(bodyBox);

        configDialog = new JFXDialog(stack, layout, JFXDialog.DialogTransition.TOP);
        closeButton.setOnAction(e -> {
            configDialog.close();
        });
        configDialog.show();
    }

    /**
     * 初始化数据
     */
    private void initData() {
        domainCombo.setDisable(true);
        runButton.setDisable(true);
        new Thread(() -> {
            try {
                List<QueryDomainListResponse.Domain> domainList = ddnsService.getDomainList();
                String ipv4Address = ddnsService.getCurrentHostIPv4();
                String ipv6Address = ddnsService.getCurrentHostIPv6ForCMD();
                Platform.runLater(() -> {
                    domainComboItems.removeAll(domainComboItems);
                    domainComboItems.addAll(domainList);
                    if (domainComboItems.size() > 0) domainCombo.setDisable(false);
                    model.setIpv4(ipv4Address);
                    model.setIpv6(ipv6Address);
                    stack.getChildren().remove(loading);
                    runButton.setDisable(false);
                    if (configDialog != null) configDialog.close();
                });
            } catch (ClientException e) {
                e.printStackTrace();
            }
        }).start();
    }

    /**
     * 关闭窗口逻辑
     */
    public void closeWindow() {
        Stage stage = (Stage) stack.getScene().getWindow();
        if (!tray.isInit()) tray.listen(stage);
        if (model.isIsShow()) {
            minimizeOrClose();
            return;
        }
        JFXDialogLayout layout = new JFXDialogLayout();
        // VBox bodyBox = new VBox();
        // bodyBox.setAlignment(Pos.BOTTOM_CENTER);
        // bodyBox.setSpacing(20);
        Label label = new Label("是否确认退出程序");
        JFXCheckBox minimizeBox = new JFXCheckBox("后台运行");
        minimizeBox.selectedProperty().bindBidirectional(model.minimizeProperty());
        JFXCheckBox showCheck = new JFXCheckBox("不再显示此窗口");
        showCheck.selectedProperty().bindBidirectional(model.isShowProperty());
        // bodyBox.getChildren().addAll(label, minimizeBox);
        // layout.setBody(bodyBox);
        layout.setBody(label);

        JFXButton confirm = new JFXButton("确认");
        confirm.setButtonType(JFXButton.ButtonType.RAISED);
        confirm.setStyle("-fx-background-color: #e7e8e8");
        JFXButton cancel = new JFXButton("取消");
        cancel.setButtonType(JFXButton.ButtonType.RAISED);
        cancel.setStyle("-fx-background-color: #e7e8e8");
        layout.setActions(minimizeBox, showCheck, confirm, cancel);

        JFXDialog dialog = new JFXDialog(stack, layout, JFXDialog.DialogTransition.CENTER);
        confirm.setOnAction(event -> {
            if (minimizeOrClose()) dialog.close();
        });
        cancel.setOnAction(event -> {
            dialog.close();
        });
        dialog.show();
    }

    /**
     * 最小化
     *
     * @param event
     */
    public void minimizeWindow(ActionEvent event) {
        Stage stage = (Stage) stack.getScene().getWindow();
        stage.setIconified(true);
    }

    /**
     * 后台或关闭程序
     *
     * @return
     */
    private boolean minimizeOrClose() {
        if (model.isMinimize()) {
            try {
                tray.show();
                tray.hide();
            } catch (AWTException e) {
                e.printStackTrace();
            }
            return true;
        }
        tray.close();
        return false;
    }


    public void showSetting() {

    }

    /**
     * 执行定时
     */
    public void runTimeTask() throws SchedulerException {
        if (scheduler == null)
            //创建scheduler
            scheduler = StdSchedulerFactory.getDefaultScheduler();
        if (job == null)
            //创建job对象
            job = JobBuilder.newJob(DDNSJob.class).build();
        job.getJobDataMap().put("model", model);
        /**创建trigger对象（简单型）**/
        Trigger trigger = TriggerBuilder.newTrigger().withSchedule(scheduleHandler().apply(timeInterval)).build();  //简单型
        // Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();  //cron型
        //装载job和trigger
        scheduler.scheduleJob(job, trigger);
        //启动调度
        scheduler.start();
        setButtonType(false);
    }

    /**
     * schedule处理器，通过判断运行周期选项确认Quartz的schedule，支持时、分、秒
     * @return
     */
    private Function<Integer, SimpleScheduleBuilder> scheduleHandler(){
        Function<Integer, SimpleScheduleBuilder> fc = null;
        switch (cycleValue.getValue()){
            case "秒":
                fc = SimpleScheduleBuilder::repeatSecondlyForever;
                break;
            case "分":
                fc = SimpleScheduleBuilder::repeatMinutelyForever;
                break;
            case "时":
                fc = SimpleScheduleBuilder::repeatHourlyForever;
                break;
            default: throw new RuntimeException("未知类型！");
        }
        return fc;
    }

    /**
     * 取消定时
     */
    public void cancelTimeTask() throws SchedulerException {
        scheduler.clear();
        setButtonType(true);
    }

}
